﻿
#pragma once

#include "sfinae_detail.h"


#define dDEDUCE_TYPE(member_name) \
    decltype(std::declval<U>().member_name)

#define dDETECTOR_MEMBER_DATA(member_name)                            \
template<class C,class T, int DATA = ::std::is_function<T>::value>    \
struct has_##member_name: ::tools::sfinae::YesNo                      \
{                                                                     \
    template<class U>static typename ::tools::sfinae::                \
        Same<T, dDEDUCE_TYPE(member_name) >::Result                   \
            Check( dDEDUCE_TYPE(member_name)* );                      \
                                                                      \
    template<class U>static No Check(...);                            \
                                                                      \
    enum { eIS_AVAILABLE = sizeof(Check<C>(0)) == sizeof(Yes) };      \
};

#define dDETECTOR_MEMBER_FUNCTION(member_name)                        \
template< class C, class T> struct has_##member_name<C,T,1>           \
    : ::tools::sfinae::SFINAE<C,T>                                    \
{                                                                     \
    typedef ::tools::sfinae::SFINAE<C,T> Parent;                      \
    typedef typename Parent::Yes Yes;                                 \
    typedef typename Parent::No No;                                   \
                                                                      \
    template<class U>static                                           \
        Yes CheckNoConst(                                             \
                Helper< Method, static_cast<Method>(&U::member_name) >* );                        \
                                                                      \
    template<class U>static                                           \
        Yes CheckConst(                                               \
                CHelper< CMethod, static_cast<CMethod>(&U::member_name) >* );                       \
                                                                      \
    template<class U>static No CheckConst(...);                       \
    template<class U>static No CheckNoConst(...);                     \
                                                                      \
    enum { eIS_NOCONST = sizeof(CheckNoConst<C>(0)) == sizeof(Yes) }; \
    enum { eIS_CONST   = sizeof(CheckConst<C>(0))   == sizeof(Yes) }; \
    enum { eIS_AVAILABLE = eIS_NOCONST || eIS_CONST };                \
};

#define DETECTED_MEMBER(member_name)                                  \
dDETECTOR_MEMBER_DATA(member_name)                                    \
dDETECTOR_MEMBER_FUNCTION(member_name)

//------------------------------------------------------------------------------------
#if 0
#define dDETECTOR_BY_NAME(member_name)                                \
template<class C,class T>                                             \
struct has_##member_name##_by_name: ::tools::sfinae::YesNo            \
{                                                                     \
    template<class U>static typename ::tools::sfinae::                \
        Same<T, dDEDUCE_TYPE(member_name) >::Result                   \
            Check( dDEDUCE_TYPE(member_name)* );                      \
                                                                      \
    template<class U>static No Check(...);                            \
                                                                      \
    enum { eIS_AVAILABLE = sizeof(Check<C>(0)) == sizeof(Yes) };      \
};

template<class C>                         
struct has_by_name: ::tools::sfinae::YesNo        
{                                                 
    template<class U>static 
    dDEDUCE_TYPE(member_name) Check( dDEDUCE_TYPE(member_name)* );              
    
    template<class U>static No Check(...);                        
    
    enum { eIS_AVAILABLE = sizeof(Check<C>(0)) == sizeof(Yes) };      
};
#endif

//------------------------------------------------------------------------------------

#ifdef DEMO_ONLINE


//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------

#define is_available(class_name, member_type, member_name)         \
    has_##member_name<class_name, member_type>::eIS_AVAILABLE

#define is_available_method(class_name, member_type, member_name)  \
    has_##member_name<class_name, member_type>::eIS_NOCONST

#define is_available_cmethod(class_name, member_type, member_name) \
    has_##member_name<class_name, member_type>::eIS_CONST

//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------


#include<iostream>
using namespace std;

int g;
struct A
{
    A():r(g){}

    void fooA(int);
    void fooA(int)const;
    void fooA();
    void fooA()const;

    int v;
    int& r;
    int a[10];
    int* p;
    int** pp;
private:
    void private_foo();
};

struct B : A
{
    int v;
    int fooB(){ return 0; }
};


DETECTED_MEMBER(fooA);
DETECTED_MEMBER(fooB);
DETECTED_MEMBER(v);
DETECTED_MEMBER(r);
DETECTED_MEMBER(a);
DETECTED_MEMBER(p);
DETECTED_MEMBER(pp);

DETECTED_MEMBER(private_foo);


int main()
{
    //синтаксис:  is_available(  имя_класса ,  тип_мембера  , имя_мембера)

    //вернет true если мембер доступен, иначе - false

    //---- детектирование данных
    cout<< "is available int B.v ? (most be 1) "
        << is_available(B,int,v)     <<endl;

    cout<< "is available int& B.r ?  (most be 1) "
        << is_available(B,int&,r)    <<endl;

    cout<< "is available int B.a[10] ?  (most be 1) "
        << is_available(B,int[10],a) <<endl;

    cout<< "is available int* B.p ?  (most be 1) "
        << is_available(B,int*,p)    <<endl;

    cout<< "is available int* B.pp ?  (most be 1) "
        << is_available(B,int**,pp)  <<endl;

    //---- детектирование методов
    cout<< "is available void B.fooA(int) or void B::fooA(int)const ?  (most be 1) "
        <<is_available(B,void(int),fooA) <<endl;

    cout<< "is available void B.fooA(int) ?  (most be 1) "
        << is_available_method(B,void(int),fooA) <<endl;

    cout<< "is available void B.fooA(int)const ?  (most be 1) "
        << is_available_cmethod(B,void(int),fooA) <<endl;

    cout<< "is available int B.fooB() ?  (most be 1) "
        << is_available_method(B,int(),fooB) <<endl;

    cout<< "is available void B.private_foo() ?  (most be 0) "    
        << is_available(B,void(),private_foo) <<endl;
}

#endif